package org.yacon;

import java.util.*;

/**
 * @author Evgeny Vetrov
 */
class HelpMessageBuilder {
    static String fullCommandDescription(ConsoleCommand command) {
        Set<String> cmdNames = command.getNames();
        Set<CommandParameter> cmdParams = command.getParams();
        String description = command.getDescription();
        return fullDescription(cmdNames, cmdParams, description, "command");
    }

    private static String fullDescription(Set<String> cmdNames, Set<CommandParameter> cmdParams, String description,
                                          String macros) {
        StringBuffer sb = new StringBuffer(1000);
        if (description != null)
            sb.append(description).append("\n");
        if (cmdNames.size() > 1)
            sb.append("To execute " + macros + ", type one of it's names");
        else
            sb.append("To execute " + macros + ", type it's name");
        if (cmdParams == null || cmdParams.isEmpty())
            return sb.toString();
        if (cmdParams.size() > 0 && cmdParams.size() <= 1)
            sb.append(" and parameters list in format paramName=paramValue");
        else if (cmdParams.size() > 1)
            sb.append(" and parameter value");
        sb.append("\n");
        String withCapitalMacros = macros.substring(0, 1).toUpperCase() + macros.substring(1);
        if (cmdNames.size() > 1)
            sb.append(withCapitalMacros).append(" names are: ").append(cmdNames);
        else
            sb.append(withCapitalMacros).append(" name is: ").append(cmdNames.iterator().next());
        sb.append("\n");
        Map<String, Set<CommandParameter>> paramsMap = Collections.singletonMap((String) null, cmdParams);
        cmdParamsDescription(null, paramsMap, sb, 1, new IntHolder(), new IntHolder());
        return sb.toString();
    }

    static String fullProgramDescription(Program program) {
        Set<String> cmdNames = program.getNames();
        ConsoleCommand initCommand = program.getInitCommand();
        Set<CommandParameter> cmdParams = Collections.emptySet();
        if (initCommand != null)
            cmdParams = initCommand.getParams();
        String description = program.getDescription();
        return fullDescription(cmdNames, cmdParams, description, "program");
    }

    private static void cmdParamsDescription(CommandParameter parent, Map<String, Set<CommandParameter>> paramsMap,
                                       StringBuffer sb, int level, IntHolder reqOffset, IntHolder optOffset) {
        for (Map.Entry<String, Set<CommandParameter>> entry : paramsMap.entrySet()) {
            if (entry.getKey() != null && parent != null) {
                String key = entry.getKey();
                reqOffset.setValue(0);
                sb.append(getOffsetString(level)).append("if value of \"").append(parent.getName()).append("\" is ")
                        .append((key.length() == 0) ? "other from previous conditions" : ("\"" + key + "\""))
                        .append(" then additional parameters are").append(
                        (entry.getValue() == null || entry.getValue().isEmpty()) ? " not needed" : ":").append("\n");
                optOffset.setValue(0);
            }
            Set<CommandParameter> commandParameters = entry.getValue();
            for (CommandParameter parameter : commandParameters) {
                String description = parameter.getDescription();
                String paramString = getOffsetString(level + 1)
                        + parameter.getName()
                        + (parameter.getType() == null ? "" : " - " + parameter.getType())
                        + (parameter.getPattern() == null ? "" : " : value pattern '" + parameter.getPattern() + "'")
                        + (description == null ? "" : ("\t-\t" + description)) + "\n";
                IntHolder offset = parameter.isRequired() ? reqOffset : optOffset;
                String typeName = parameter.isRequired() ? "required" : "optional";

                if (offset.getValue() <= 0) {
                    sb.append(getOffsetString(level)).append(typeName).append(" parameters:\n");
                    offset.setValue(sb.length());
                }
                sb.insert(offset.getValue(), paramString);
                offset.setValue(paramString.length() + offset.getValue());
                Map<String, Set<CommandParameter>> innerParams = parameter.getParams();
                if (innerParams != null && !innerParams.isEmpty())
                    cmdParamsDescription(parameter, innerParams, sb, level + 1, reqOffset, optOffset);

            }
        }
    }

    private static String getOffsetString(int level) {
        char[] result = new char[level];
        Arrays.fill(result, '\t');
        return new String(result);
    }

    private static class IntHolder {
        private int value;


        public int getValue() {
            return value;
        }

        public void setValue(int value) {
            this.value = value;
        }
    }
}